/**
 * 多比例图片裁剪上传组件
 * 用户在指定弹出modal内可以自由裁剪图片并上传
 *
 * > 依赖 cropper/bootstrap 实现裁剪
 * > 依赖部分签名js 以及自定义的uploader组件上传图片
 * > 此插件由于是使用上传id在本地签名，仅内部使用
 */
(function(){
    /**
    * modal模版
    */
    var modalHtml = '<div class="modal fade" id="{{id}}" role="dialog" aria-labelledby="modalLabel" tabindex="-1">\
        <div class="md-doc modal-dialog modal-lg" role="document">\
            <div class="modal-content">\
                <div class="modal-header">\
                    <h5 class="md-title modal-title"></h5>\
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>\
                </div>\
                <div class="modal-body">\
                    <div class="md-wrap clip-wrap">\
                        <div class="clip-main">\
                            <div class="clip-canvas">\
                                <div class="img-container">\
                                    <img class="md-img" src="" alt="Picture">\
                                </div>\
                            </div>\
                            <div class="clip-btns">\
                                <div class="md-special-file pull-left mr10">\
                                    <label class="btn btn-primary" for="fileInput1">\
                                        <input type="file" class="sr-only md-file-input" id="fileInput1" name="file">\
                                        <span>选择图片</span>\
                                    </label>\
                                </div>\
                                <span class="md-notice"></span>\
                                <div class="pull-right">\
                                    <button type="button" class="md-exit-btn btn btn-default">取消</button>\
                                    <button type="button" class="md-clip-btn btn btn-primary">确定裁剪</button>\
                                </div>\
                            </div>\
                        </div>\
                        <div class="md-clip-handle clip-handle">\
                            <div class="clip-split">\
                                <div class="md-btn-group btn-group-vertical">\
                                </div>\
                                <div class="clip-handle-bottom">\
                                    <label class="btn btn-primary" for="fileInput2">\
                                        <input type="file" class="sr-only md-file-input" id="fileInput2" name="file">\
                                        <span>选择图片</span>\
                                    </label>\
                                    <label class="md-clip-sub btn btn-primary clip-sub" >\
                                        <span title="上传图片" data-original-title="submit checked picture">上传图片</span>\
                                    </label>\
                                </div>\
                            </div>\
                            <div class="md-clip-view clip-view"></div>\
                        </div>\
                    </div>\
                </div>\
            </div>\
        </div>\
        <div class="cover"><span>请稍候...</span></div>\
    </div>';

    /**
    * cache
    */
    var modalCache = {
        count: 0, // 总计多少个modal
    }

    /**
    * 默认配置
    */
    var modalConfig = {
        modalId: 'clipModal', // 默认modal的id
        console:true, //是否在控制台显示console
        area:[568,419], //裁剪容器尺寸 这一点和在样式中设定的尺寸保持一致，不要更改
        maxBtn:10,  //最大按钮数量
        special: false, // 是否在配置只有一个时显示为单张上传模式
    }

    /**
    * 上传默认配置
    */
    var defaultUploadConfig = {
        host: { // 上传图片地址
            'oss': null, 
            'qn': null
        },
        accessid: null, // ali上传图片相关权限id
        accesskey: null, // ali上传图片相关权限key
        action: 'normal',
        filename: 'test.jpg'
    }

    /**
    * 裁剪默认配置
    */
    var defaultClipOption = {
        // 静态文字
        title:"裁剪图片",//title文字
        // 默认图片
        blank:"",//裁剪区域加载失败显示的默认背景图
        preview:"",// 预览区域图片加载失败或默认显示的图图片
        // 上传配置
        subType: 'oss', // 上传方式 阿里为oss 七牛为qn 默认为oss
        autoJoin: false, //是否在检测到手选图片合适时跳过裁剪
        type:"jpg,jpeg,png,gif,bmp,tiff",//允许上传的图片后缀
        size:2//大小(MB)
    }

    /**
    * 工具方法
    */

    //转化blob对象为dataurl
    var readBlobAsDataURL = function(blob, callback) {
        var a = new FileReader();
        a.onload = function(e) {callback(e.target.result);};
        a.readAsDataURL(blob);
    }

    // 获取a和b的最大公约数
    var getMaxDivisor = function(a,b){
        if(b===0){return a;}
        return getMaxDivisor(b,a%b);
    }

    /**
    * 创建裁剪实例
    * @param {object} clipOpt 裁剪配置
    * @param {object} uploadOpt 上传配置
    */
    function ComicClip(clipOpt, uploadOpt) {
        // 1.检测依赖
        if(!$) {
            throw Error('没有找到jquery');
        }
        if(!Cropper) {
            throw Error('没有找到Cropper');
        }


        // 2.参数配置
        if(clipOpt && typeof clipOpt !== 'object'){
            throw Error('裁剪配置options必须为对象');
        }
        if(uploadOpt && typeof uploadOpt !== 'object'){
            throw Error('上传配置options');
        }
        this.modalId = nowId = modalConfig.modalId + (++modalCache.count);

        // 插入html/节点缓存
        this._dom = {}; // 缓存dom节点
        this._dom.body = $('body'); // 节点：body
        this._dom.body.append(modalHtml.replace('{{id}}', this.modalId));
        
        this._dom.modal =$('#' + this.modalId);//容器
        this._dom.doc = this._dom.modal.find(".md-doc");//弹窗包裹器
        this._dom.title = this._dom.modal.find(".md-title");//容器标题
        this._dom.notice = this._dom.modal.find(".md-notice");//容器标题
        this._dom.clipWrap = this._dom.modal.find(".md-wrap");//裁剪板块
        this._dom.img = this._dom.modal.find(".md-img");//用于初始化cropper的图片
        this._dom.specialFile = this._dom.modal.find(".md-special-file ");//特殊模式下的input
        this._dom.exitBtn = this._dom.modal.find(".md-exit-btn");//取消裁剪按钮
        this._dom.clipBtn= this._dom.modal.find(".md-clip-btn");//确认裁剪按钮
        this._dom.clipHandle= this._dom.modal.find(".md-clip-handle");//工具和预览板块
        this._dom.btnWrapper= this._dom.modal.find(".md-btn-group");//裁剪方式按钮组
        this._dom.fileInput= this._dom.modal.find(".md-file-input");//裁剪input
        this._dom.sub= this._dom.modal.find(".md-clip-sub");//提交所有按钮
        this._dom.viewWrapper= this._dom.modal.find(".md-clip-view");//预览组容器
        this._dom.btnList= '.clip-btn-item';//按钮
        this._dom.viewList= '.clip-view-item';//预览容器
        this._dom.cover = this._dom.modal.find('.cover'); // 遮罩

        // 整合配置
        this.clipOptions = $.extend(true, {}, defaultClipOption, clipOpt);
        this.uploadOptions = $.extend(true, {}, defaultUploadConfig, uploadOpt);

        // host必须/尾缀
        if(this.uploadOptions.host) {
            if(this.uploadOptions.host['oss'] && this.uploadOptions.host['oss'].slice(-1) !== '/'){
                this.uploadOptions.host['oss'] += '/';
            }
            if(this.uploadOptions.host['qn'] && this.uploadOptions.host['qn'].slice(-1) !== '/'){
                this.uploadOptions.host['qn'] += '/';
            }
        } else {
            throw Error('没有传入host');
        }

        // 全局状态属性
        this._cachePic = []; // 缓存所有成功上传的图片
        this._callback = function(){}; // 所有图片上传回调，在调用use的时机可以重置一次
        this._index = null; // 当前裁剪尺寸下标
        this._opened = false; // 当前是否打开状态

        // 缓存裁剪队列
        this._queue = {};

        // 静态文字修改
        this.clipOptions.notice = "只支持"+this.clipOptions.type+",大小不超过"+this.clipOptions.size+"M";
        this.dom('modal').hide();
        this.dom("title").text(this.clipOptions.title);
        this.dom("notice").text(this.clipOptions.notice);

        // event
        var self = this;

        // bootstrap隐藏回调:调用一次triggleClose
        this.dom("modal").on('hidden.bs.modal', function () {
            self.triggerClose();
        });

        //取消按钮关闭modal
        this.dom("exitBtn").on("click",function(){
            self.triggerClose(true);
        });

        // 切换按钮:按钮组中按钮点击切换种类
        this.dom("btnWrapper").on("click",".clip-btn-item",function(){
            var _t=$(this);
            var index=_t.index();
            if(!_t.hasClass("active")){
                self.active(index).reload(index);
            }
        });

        // 预览框:预览框处理情况和按钮组相同
        this.dom("viewWrapper").on("click",".clip-view-item",function(){
            var _t=$(this);
            var index=_t.parent().index();
            if(!_t.hasClass("active")){
                self.active(index).reload(index);
            }
        });

        // 裁剪按钮:确定裁剪按钮触发裁剪
        this.dom("clipBtn").on("click",function(){
            //在特殊模式下此按钮的功能变为裁剪后立即提交
            if(modalConfig.special){
                self.clip(function(){
                    //确定裁剪后自动填充裁剪结果到cropper,并尝试提交图片
                    self.active(self._index).reload(self._index,function(){
                        self._openLoading();
                        setTimeout(function(){
                            self.dom("sub").click()
                            self._closeLoading();
                        },200)
                    });
                });
            }
            else {
                self.clip(function(){
                    //确定裁剪后自动填充裁剪结果到cropper
                    self.active(self._index).reload(self._index);
                });
            }
        });

        // 提交按钮:提交所有已裁剪的图片
        this.dom("sub").on("click",function(){
            //检查cache中是否有保存的blob
            var mask=false;
            self._queue.forEach(function(t){
                if(t.cache.blob){
                    mask=true;
                }
            });
            if(mask){
                if(confirm("确定要提交已经裁剪过的图片吗？")){
                    self.submit();
                }
            }
            else {
                alert("您还没有保存过任何裁剪后的图片！");
                return this;
            }
        });

        // 初始化cropper及相关变量
        this.clipImage = this.dom("img")[0];
        this.urlTool = window.URL || window.webkitURL; // url工具
        this._cacheUrlLink = null; // 缓存url对象生成的图片blob路径
        this._clipImageType = 'png'// 导出图片数据的格式
        this.cropperOpt = {
            container: '.img-container',
            image: this.clipImage,
            aspectRatio:1,
            dragMode:"move",
            cropBoxMovable:false,
            cropBoxResizable:false,
            cropperDataType: false,
            minCropBoxWidth:99999,//插件内部 做了边界判断，设为较大值是为了让裁剪框最大边抵拢边框
            actions: '#actions',
            minContainerWidth:modalConfig.area[0],
            minContainerHeight:modalConfig.area[1],
        };
        this.cropper = new Cropper(this.dom("img")[0], this.cropperOpt);

        // 初始化上传插件
        this.uploaders = {};
        this.uploaderInited = false;
        this.initUploaders();

        //为input file 绑定切换图片功能
        this.dom("fileInput").each(function(){
            self.bindFileInput(this,function(file,success,defeat){
                //开启异步遮罩，通过载入此图片获取图片宽高判断
                self._openLoading();
                //验证图片大小，格式和最小宽高是否符合要求
                var size=file.size/1024/1024;
                var type=file.type.match(/\w+$/)[0],typeAry=self.clipOptions.type.split(",");
                if(self.urlTool){//支持URL接口才验证宽高
                    var data=self._queue[self._index];
                    var url=self.urlTool;
                    var imgSrc = url.createObjectURL(file);
                    var img= new Image();
                    var imgH,imgW;
                    img.onload=function(e){
                        imgH=this.height;
                        imgW=this.width;
                        url.revokeObjectURL(imgSrc);
                        //进行判断
                        if(size>self.clipOptions.size){
                            defeat();
                            self._alert("选择图片失败，图片大小不能大于"+self.clipOptions.size+"M");
                            self._closeLoading();
                        }
                        else if($.inArray(type,typeAry)===-1){
                            defeat();
                            self._alert("选择图片失败，仅限格式为："+self.clipOptions.type+"的图片");
                            self._closeLoading();
                        }
                        else if(imgW<data.param.clipsize[0]||imgH<data.param.clipsize[1]){
                            defeat();
                            self._alert("选择图片失败，请选择像素宽高大于"+data.param.clipsize[0]+"x"+data.param.clipsize[1]+"的图片！")
                            self._closeLoading();
                        }
                        else {
                            success();
                            self._closeLoading();
                            //检测到图片刚好符合大小
                            if(self.clipOptions.autoJoin&&imgW===data.param.clipsize[0]&&imgH===data.param.clipsize[1]){
                                self._confirm('您选择的图片恰好满足裁剪尺寸，是否直接保存图片？',function(){
                                    readBlobAsDataURL(file,function(dataUrl){
                                        self.skipClip(dataUrl,file);
                                    });
                                });
                            }
                        }
                    };
                    img.onerror=function(){
                        this.onload = null;
                        this.src = '';
                        defeat();
                        self._closeLoading();
                    };
                    img.src=imgSrc;
                }
                return true;
            });
        });

        // error处理
        this._errorAry = [];
        this._error = function(v){
            this._errorAry.push(v);
            alert(v);
            // this._closeLoading();
        }
    }

    // 基本原型方法
    var baseMethod = {
        /**
        * 获取内部缓存的dom节点
        * @param {string} name 节点标记
        */
        dom: function(name) {
            var d=this._dom[name];
            if(typeof d==="string"){
                d=$(d);
            }
            return d;
        },
        /**
        * 关闭控制，触发_callback，抛出已完成的图片
        * @param {boolean} handle 是否主动关闭modal
        */
        triggerClose: function(handle) {
            this._opened = false;
            this._callback(this._cachePic);
            !!handle&&this._hideModal();
            return this;
        },
        /**
        * 指定index重载cropper
        * @param {number} index 载入的下标
        * @param {function} callback 回调
        */
        reload: function(index, callback) {
            var i,call,self=this;
            i=index?index:0; // 默认第一个
            call=typeof callback==="function"?callback:function(){};
            var data=this._queue[i];
            if(!data){
                return this;
            }
            if(data.cache.blob){ // 已有图片
                //如果有缓存的blob，优先读取blob到裁剪中
                this.clipReload(data.cache.blob,{
                    aspectRatio:data.param["clipsize"][0]/data.param["clipsize"][1]
                },function(){
                    self._index=i;
                    call();
                });
            }
            else { // 图片为路径
                //这里先执行数据中src的读取操作，如果图片加载失败，使用默认的预览图
                var ni=new Image();
                var nowHost = data.param.previewHost || self.uploadOptions.host[data.param.subType];
                var nowSrc = nowHost ? nowHost + data.param.pathname : self.clipOptions.blank;
                var callFn=function(src){
                    // self._comicCropper.reload(src,{
                    //     aspectRatio:data.param["clipsize"][0]/data.param["clipsize"][1],
                    //     ready:function(){
                    //         self._closeLoading();
                    //         call();
                    //         self._index=i;
                    //     }
                    // });
                    //这里有个问题，cropper对于图片自己加了随机值，所以即使做了预加载，还是会重新载入一次，会引起裁剪不到图片
                    //所以这里强行将canvas里面的那张加了随机数的src，成功后才关闭遮罩，避免裁剪出空白
                    self.clipReload(src,{
                        aspectRatio:data.param["clipsize"][0]/data.param["clipsize"][1]
                    },function(){
                        var afterSrc=self.dom("img").siblings(".cropper-container").find('.cropper-canvas > img').attr("src");
                        var ni2=new Image();
                        ni2.onload=function(){//由于已经预加载过了，不用担心error
                            self._closeLoading();
                            self._index=i;
                            call();
                        };
                        ni2.onerror=function(){//失败了卧槽???
                            this.onload = null;
                            this.src = '';
                            self._error("加载图片异常，请检查默认的blank图片是否存在！");
                        };
                        ni2.src=afterSrc;
                    });
                };
                ni.onload=function(){
                    callFn(nowSrc);
                };
                ni.onerror=function(){
                    this.onload = null;
                    this.src = '';
                    callFn(self.clipOptions.blank);
                };
                this._openLoading();
                ni.src= nowSrc;
            }
            return this;
        },
        /**
        * 执行裁剪操作(将当前cropper中的图片裁剪并缓存)
        * @param {function} callback 回调
        */
        clip:function(callBack){
            this._openLoading();
            var self=this;
            this.getClipData("blob",function(blob){
                if(!blob){
                    self._error("目前裁剪区域没有图片！");
                    self._closeLoading();
                    return false;
                }
                var data=self._queue[self._index];
                //右边预览框载入base64数据图片
                readBlobAsDataURL(blob,function(dataUrl){
                    self.dom("viewList").find("img").eq(self._index).attr("src",dataUrl);
                    typeof callBack==="function"&&callBack();
                });
                //缓存blob到cache中
                data.cache.blob=blob;
                self._closeLoading();
            });
            return this;
        },
        /**
        * 直接保存某张图片
        */
        skipClip:function(img,file){
            this.dom("viewList").find("img").eq(this._index).attr("src",img);
            this._queue[this._index].cache.blob=file;
            return this;
        },
        /**
        * 提交当前缓存的所有图片 *核心方法
        */
        submit:function(){ //提交保存的所有内容
            //循环提取多个数据
            var subAry=[],self=this;
            this._queue.forEach(function(t){
                if(t.cache.blob){
                    subAry.push({
                        text:t.param.clipname,
                        host:t.param.previewHost||self.uploadOptions.host[t.param.subType],
                        blob:t.cache.blob,
                        name:t.param.name,
                        dir:t.param.dir,
                        path:t.param.pathname,
                        size:t.param.clipsize,
                        subType: t.param.subType,
                        action: t.param.action,
                        img:t.cache.img[0],//由于只有经过裁剪的才会被提取，所以这里的img必然是baseurl而不是缩放后的图片
                        exp:null
                    });
                }
            });
            if(subAry.length===0){
                this._alert("您还没有保存过任何裁剪后的图片！");
                return this;
            }
            //对每个数据进行缩放操作，使它保持指定的像素宽高
            var blobData,cv,max=subAry.length,count=0;
            this._openLoading();

            subAry.forEach(function(t){
                blobData=t.blob;
                //1.创建一个指定size的canvas
                cv=document.createElement("canvas");
                var tmpW = Math.ceil(t.size[0]);
                var tmpH = Math.ceil(t.size[1]);
                cv.width = tmpW;
                cv.height = tmpH;
                //2.将img节点绘制到canvas中
                cv.getContext('2d').drawImage(t.img, 0, 0, tmpW, tmpH);
                cv.toBlob(function(b){
                    count++;
                    t.exp=b;
                    if(count===max){
                        doSend();
                    }
                }, t.blob.type); // 记得缓存为适当的格式
            });
            
            //3.提交整理后的blob数据
            function doSend(){
                self._closeLoading();
                //实现多个ajax依次回调执行，即使失败，下一个ajax依然会执行
                var data,formData,errorAry=[],successAry=[];
                self._cachePic = []; // 重置提交成功图片
                //最后一张图片上传完毕执行回调
                var postEnd=function(){
                    if(errorAry.length==0){
                        self._alert("上传成功!");
                    }
                    else if(errorAry.length<subAry.length){
                        var erStr='部分图片上传失败，失败的图片分别为';
                        errorAry.forEach(function(t){
                            erStr+=t.text+'，';
                        });
                        erStr+='。';
                        self._alert(erStr);
                    }
                    else {
                        self._alert("上传失败，请检查网络!");
                    }
                    //设置当前显示图片为第一张
                    successAry.forEach(function(o){
                        self._cachePic.push(o);
                    });
                };
                var countAjax=function(){
                    countAjax._count++;
                    var i=countAjax._count;
                    var max=countAjax._max;
                    if(max<i){
                        self._closeLoading();
                        postEnd();
                        countAjax = null;
                        return false;
                    }
                    data=subAry[i-1];
                    // 确定哪种方式上传
                    if(data.subType === 'oss') {
                        self.uploadOssData({
                            host: data.host,
                            accessid: self.uploadOptions.accessid,
                            accesskey: self.uploadOptions.accesskey,
                            filename: data.name,  // 上传文件名称可以在这里自己指定，默认为上传图片名称
                            dirname: data.dir || '' // 上传目录
                        }, data.exp, function(status, res){
                            if(status) {
                                successAry.push(data.host + data.path);
                            } else {
                                errorAry.push(data.host + data.path);
                            }
                            countAjax();
                        })
                    } else if(data.subType === 'qn'){
                        self.uploadQnData({
                            action: data.action || this.uploadOptions.action,
                            filename: data.name,  // 上传文件名称可以在这里自己指定，默认为上传图片名称
                            dirname: data.dir || '' // 上传目录
                        }, data.exp, function(status, res){
                            if(status){
                                successAry.push(data.host + data.path);
                            } else {
                                errorAry.push(res);
                            }
                            countAjax();
                        });
                    } else {
                        self._alert('没有找到正确的上传方式');
                    }
                };
                countAjax._count=0;
                countAjax._max=subAry.length;
                self._openLoading();
                countAjax();
            }
            return this;
        },
        
        /**
        * 通过传入的配置初始化并弹出裁剪弹窗 *核心方法
        * 传参示例 [{
        *       "clipname": "比例1：1",
        *       "clipsize": [50, 50],
        *       "pathname": "img/test3.jpg",
        *       "previewHost": "",
        *       "suffix": ""
        *     }]
        * @param {object} config 配置列表
        * @param {function} callback 弹出回调 接收两个参数isSub(是否完成了上传图片) pic(如果完成了上传图片，会返回第一个尺寸的图片)
        */
        use:function(config,callBack){
            if(this._opened) {
                return false;
            }
            this._opened = true;
            if(Object.prototype.toString.call(config)!=="[object Array]"){
                this._error("数据格式有误!");
                return this;
            }
            if(config.length===0){
                this._error("缺少数据！");
                return this;
            }
            if(config.length>modalConfig.max){
                config.length=7;
                this._error("最多支持7组数据！");
                return this;
            }
            if(typeof callBack==="function"){ // 设置关闭弹窗时的
                this._callback=callBack;
            }
            //存入option
            var self=this,obj;
            self._queue=[];
            self._cachePic = []; // 重置提交成功图片
            config.forEach(function(t){
                obj={
                    param:t,
                    cache:{
                        img:null,//储存预览img
                        blob:null//用于储存临时保存的blob
                    }
                };
                //从pathname中解析出文件名称和路径
                if(obj.param.pathname && obj.param.pathname.slice(0,1)==='/'){ // 去掉/前缀
                    obj.param.pathname = obj.param.pathname.slice(1);
                }
                var parsePath = obj.param.pathname.match(/^[\s\S]*\//);
                var parseName = obj.param.pathname.match(/[^\/]*?$/);
                if(!parseName) {
                    throw Error('pathname有误')
                }
                obj.param.name= parseName ? parseName[0] : 'default.jpg';
                if(parsePath.length) {
                    obj.param.dir= parsePath[0];
                } else {
                    obj.param.dir= ''
                }
                self._queue.push(obj)
            });
            //构建html
            this.whiteHtml();
            //按照新数据重载cropper 默认载入第一条数据的图片
            var showSpecial = modalConfig.special&&config.length===1
            this.reload(0,function(){
                //显示modal
                self._showModal(!!showSpecial);
            });
            this.active(0);
            return this;
        },
        //根据_queue构建按钮组和预览组
        whiteHtml:function(){
            if(this._queue.length){
                var self=this;
                var buttonHtml='',viewHtml='',tempData,tempAry,nowHost,i=0;
                var rdNum=parseInt(Math.random()*1000000);
                this._queue.forEach(function(t){
                    nowHost = t.param.previewHost || self.uploadOptions.host[t.param.subType];
                    i++;
                    tempData=t.param;
                    //通过最大公约数获取比例
                    tempAry=[];
                    tempAry[0]=getMaxDivisor(tempData["clipsize"][0],tempData["clipsize"][1]);
                    tempAry[1]=tempData["clipsize"][0]/tempAry[0];
                    tempAry[2]=tempData["clipsize"][1]/tempAry[0];
                    buttonHtml+='<label class="btn btn-primary clip-btn-item">' +
                        '<span> '+tempData["clipname"]+' </span>\n' +
                        '</label>';

                    viewHtml+='<div class="clip-view-row toggle-wrap">' +
                        '<div class="clip-view-box clip-view-item">' +
                        '<img class="clip-view-img'+i+'" src="'+ (nowHost ? nowHost + tempData["pathname"]+ tempData["suffix"] + '?' + rdNum : self.clipOptions.blank) + '" alt="">' +
                        '</div>' +
                        '<div class="clip-view-des">'+tempAry[1]+' : '+tempAry[2]+'</div>' +
                        '</div>';
                    t.cache.img=".clip-view-img"+i;
                });
                this.dom("btnWrapper").html(buttonHtml);
                this.dom("viewWrapper").html(viewHtml);
                //转化所有的cache img 为真实的dom对象
                this._queue.forEach(function(t){
                    t.cache.img=$(t.cache.img);
                });
            }
            else {
                this.dom("btnWrapper").html();
                this.dom("viewWrapper").html();
            }
        },
    }

    // ui操作
    var uiMethod = {
        /**
        * 打开modal 会根据传入的状态值决定打开单文件还是多文件
        */
        _showModal:function(isSpc){
            //针对isSpc参数显示不同的节点
            this.dom('doc')[isSpc?'removeClass':'addClass']('modal-lg')
            this.dom('clipWrap')[isSpc?'addClass':'removeClass']('clip-wrap-special')
            this.dom('specialFile')[isSpc?'show':'hide']()
            this.dom('clipHandle')[isSpc?'hide':'show']()

            this.dom("modal").modal("show");//这里是用的bootstrap的显示方法
        },
        /**
        * 关闭modal
        */
        _hideModal:function(){
            this.dom("modal").modal("hide");//这里是用的bootstrap的显示方法
        },
        /**
        * 关闭loading遮罩
        */
        _closeLoading:function(){
            this._dom.cover.css({"background-color":"transparent"}).hide();
            return this;
        },
        /**
        * 打开loading遮罩
        */
        _openLoading:function(){
            this._dom.cover.css({"background-color":"rgba(0,0,0,0.3)"}).show();
            return this;
        },
        //同步弹窗
        _alert:function(msg){
            //这里使用window.alert
            alert(msg);
            return this;
        },
        //同步confirm
        _confirm:function(msg,fn){
            //这里使用window.alert
            confirm(msg)&&fn();
            return this;
        },
        //将焦点对准其中一个尺寸 会将样式调整为指定位置，并不会调整cropper裁剪源数据
        //index:序号
        active:function(index){
            var i=index?index:0;
            this.dom("btnList").removeClass("active").eq(i).addClass("active");
            this.dom("viewList").removeClass("active").eq(i).addClass("active");
            return this;
        }
    }

    // 裁剪相关原型方法
    var clipMethod = {
        
        /*
        * 解析传入的图片并把图片类型存入_clipImageType
        * img [string/file/blob] 要重载的图片
        * 返回值：boolean 是否解析成功
        * */
        parseImgType: function(img){
            var self = this;
            if(typeof img ==="string"){
                this.clipImage.src = img;
                img.replace(/\.(.*?)$/, function(res, $1){
                    self._clipImageType = $1;
                });
                if(this._clipImageType==="jpg"){
                    this._clipImageType="jpeg";
                }
            }
            else {
                var URL = this.urlTool;
                if(URL){
                    if (this._cacheUrlLink) {
                        URL.revokeObjectURL(this._cacheUrlLink);
                    }
                    this.clipImage.src = this._cacheUrlLink = URL.createObjectURL(img);
                    if(img.type) {
                        img.type.replace(/\/(.*?)$/, function(res, $1){
                            self._clipImageType = $1;
                            return res;
                        });
                    } else {
                        this._clipImageType = "png";
                    }
                }
                else {
                    console.error("浏览器不支持对应url对象！");
                    return false;
                }
            }
            return true;
        },
        /**
        * 获取当前裁剪区域内容
        * type: [string] 'blob/dataUrl' default:blob
        * callback:回调
        */
        getClipData: function(type, callback) {
            if(!this.cropper){
                console.error("cropper未初始化!");
                return this;
            }
            if(typeof callback!=="function"){
                console.error("缺少回调!");
                return this;
            }
            var opt={
                fillColor: "rgba(0,0,0,0)" //强制图片外的地方透明
            };
            //如果图片类型为jpg，背景为白色
            if (this._clipImageType === 'jpeg') {
                opt.fillColor='#fff';
            }
            var res = null;
            try {
                res=this.cropper["getCroppedCanvas"](opt);
            } catch(e){};
            
            if (res) {
                // 获得图片上传数据
                if (type==="dataUrl") {
                    callback(res.toDataURL('image/'+this._clipImageType));
                } else {
                    // 修改获取图片类型，详见: toBlob, 第三个参数可设置质量0-1之间
                    // https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toBlob
                    res.toBlob(function (file) {
                        callback(file)
                    }, this._clipImageType);
                }
            } else {
                callback(null)
            }
            return this;
        },
        /**
        * 调整cropper参数并重载
        * method:要让cropper执行的方法
        * option:首要参数对象(可选)
        * secOption:次要参数对象(可选)
        */
        clipChange: function(method,option,secOption) {
            if(!this.cropper){
                console.error("cropper未初始化!");
                return this;
            }
            if(typeof method!=="string"){
                console.error("change方法参数错误！");
            }
            var opt=option?option:{};
            var secOpt=secOption?secOption:{}; // 暂时未使用，后面扩展功能可能会用到

            var temp1,temp2,temp3,temp4;
            var self=this;
            //参数进一步处理
            switch (method) {
                case 'aspectRatio': // 改变长宽比
                    temp1 = this.cropper.getCropBoxData();
                    temp2 = this.cropper.getCanvasData();
                    this.cropperOpt.aspectRatio=opt;
                    var cacheFn=self.cropperOpt.ready;
                    this.cropperOpt.ready=function() { // 仅在本次重载后重设尺寸且不执行ready
                        self.cropper.setCropBoxData(cropBoxData).setCanvasData(canvasData);
                        self.cropperOpt.ready = cacheFn;
                    };
                    self.clipReload();
                    return this;
                    break;
                case 'rotate':
                    // this.cropper.clear();
                    break;
            }
            //执行方法
            this.cropper[method](opt,secOpt);
            //善后处理
            switch (data.method) {
                case 'rotate':
                    // this.cropper.crop();
                    break;
                case 'scale':
                case 'scaleX':
                case 'scaleY':
                    // target.setAttribute('data-option', -data.option);
                    break;
            }
            return this;
        },

        /**
        * 重设cropper为新的file、(会销毁实例并应用新的option)
        * img [string/file/blob] 要重载的图片
        * opt 重设参数
        * callback 如果设置了这个 那么opt.ready将会被替换
        */  
        clipReload:function(img,opt,callback){
            if(!this.cropper){
                console.error("cropper未初始化!");
                return this;
            }
            // var uploadedImageURL = _this.uploadedImageURL;
            if(img){
                if(!this.parseImgType(img)){
                    return this;
                }
            }

            if(typeof opt==="object") {
                this.cropperOpt = $.extend(true, {}, this.cropperOpt, opt)
            }
            // 重置ready
            if(typeof callback === 'function') {
                this.cropperOpt.ready = callback;
            }
            this.cropper.destroy();
            this.cropper = new Cropper(this.clipImage, this.cropperOpt);
            return this;
        },
        /**
        * 为某个file input绑定change事件，使其每次改变内容都会触发一次clipReload
        * fileDom：input原生对象 verify：验证函数，只有此函数返回false才会执行clipReload
        */
        bindFileInput:function(obj,verify){
            if(!this.cropper){
                console.error("cropper未初始化!");
                return this;
            }
            var URL = this.urlTool,self=this;
            var verifyFn=typeof verify==="function"?verify:function(){};
            if (URL) {
                obj.onchange = function() {
                    var files = this.files;
                    var file;
                    var _t=this;
                    if (files && files.length) {
                        file = files[0];
                        if (/^image\/\w+/.test(file.type)) {
                            //抛出两个方法给verify函数用于确定验证成功或失败
                            verifyFn(file,function(){
                                self.clipReload(file);
                            },function(){
                                //暂无失败处理
                            });
                        }

                        this.value="";
                    }
                };
            } else {
                obj.disabled = true;//禁止file input的点击
                // obj.parentNode.className += ' disabled';
                console.error("浏览器不支持对应url对象！");
                return this;
            }
        }
    }

    // 上传相关方法
    var uploadMethod = {
        // 初始化各个上传组件
        initUploaders: function(){
            var self = this;
            this.uploaders['qn'] = new PluploadUpload(null, 'qn', {
                action: this.uploadOptions.action,
                filename: this.uploadOptions.filename
            });
            this.uploaders['qn'].onInit(function(){
                self.uploaderInited = true;
            })
        },
        // oss 上传
        uploadOssData: function(opt, data, callback){
            var call = typeof callback === 'function' ? callback : function(){};
            var policyText = {
                expiration: new Date(new Date().getTime() + (opt.uploadExpire|| 36e5)), // 设置该Policy的失效时间，超过这个失效时间之后，就没有办法通过这个policy上传文件了
                conditions: [
                    ['content-length-range', 0, (opt.uploadMaxSize || 1048576000)] // 设置上传文件的大小限制
                ]
            };
            var message = Base64.encode(JSON.stringify(policyText));
            var bytes = Crypto.HMAC(Crypto.SHA1, message, opt.accesskey, {asBytes: true});
            var signature = Crypto.util.bytesToBase64(bytes);

            // 设置上传图片名字
            var filename = opt.filename || 'default.jpg';
            // 上传更目录不填写，存在目录以: **/**/方式构建目录
            var dirname = (function(dirname, filename) {
                if (!dirname) return '';
                return (dirname.lastIndexOf('/') >= 0 ? dirname : dirname + '/')
            })(opt.dirname, filename);
            
            var formData = new FormData();
            // 将配置追加到formData数据中
            $.each({
                filename: filename,
                key: dirname + filename,
                policy: message,
                success_action_status: opt.success_action_status || '200', // 让服务端返回200,不然，默认会返回204
                OSSAccessKeyId: opt.accessid,
                signature: signature
            }, function(key, item) {
                formData.append(key, item)
            });
            // 获取到formData数据，可提POST提交套服务器
            formData.set('file', data, filename);
            $.ajax({
                url:opt.host,
                type: 'post',
                contentType: false,
                processData: false,
                async: false, //  不发送同步请求
                cache: false, // 设置为false将不会从浏览器缓存中加载请求信息
                data: formData,
                success: function (res, req) {
                    call(true, res);
                },
                error: function (er) {
                    call(false, er);
                }
            });
        },

        // 七牛上传
        uploadQnData: function(opt, data, callback){
            if(!this.uploaderInited){
                this._alert('七牛上传尚未初始化或初始化失败，请稍候再试');
                return false;
            }
            var self = this;
            var fileType = 'image/' + this._clipImageType;
            var filePath = opt.dirname + opt.filename;
            
            var newFile = new File([data], filePath, {
                type: fileType
            });
            this.uploaders['qn'].change({
                action: opt.action,
                filename: filePath,
                callback: function(status) {
                    callback(status);
                }
            }, function(status, data){
                if(status) {
                    self.uploaders['qn'].upload(newFile, filePath);
                } else {
                    callback(false, '上传失败：改变配置失败');
                }
            })
        }
    }

    ComicClip.prototype = $.extend(true, {}, baseMethod, uiMethod, clipMethod ,uploadMethod);
    window['ClipModal'] = ComicClip;
})();